package com.jisc.adaptor.datasources.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

public abstract class SpreadsheetHandler {

	private Data data;
	
	public SpreadsheetHandler(Data data) {
		this.data = data;
	}
	
	public Data getData() {
		return data;
	}
	
	public void addRow(List<String> row) {
		this.getData().addRow(row);
	}
	
	public List<String> getHeader() {
		return this.getData().getHeader();
	}
	
	public void deleteRowsWithValues(String[] columns, String[] values, String condition) {
		int[] indices = this.convertFromColumnsToIndices(columns);
		this.getData().deleteRowsWithValueAtIndex(indices, values, condition);
	}
	
	public List<List<String>> getRowsWithValues(String[] columns, String[] values, String condition) {
		int[] indices = this.convertFromColumnsToIndices(columns);
		return this.getData().getRowsWithValueAtIndices(indices, values, condition);
	}
	
	public void updateRowsWithValues(String[] columns, String[] values, String condition, String targetColumns[], String newValues[]) {
		int[] indices = this.convertFromColumnsToIndices(columns);
		this.getData().updateRowsWithValueAtIndex(indices, values, condition, targetColumns, newValues);
	}
	
	private int[] convertFromColumnsToIndices(String[] columns) {
		int[] retArray = new int[columns.length];
		for (int i = 0; i < retArray.length; i++) {
			retArray[i] = this.getData().getIndexForHeaderName(columns[i]);
		}
		return retArray;
	}
	
	public abstract void save() throws Exception;
	
	public static class Data {
		
		private Map<String, Integer> header = new HashMap<String, Integer>();
		
		private List<List<String>> rows = new ArrayList<List<String>>();
		
		public Data(List<String> header) {
			int c = header.size();
			for (int i = 0; i < c; i++) {
				this.header.put(header.get(i), i);
			}
		}
		
		public List<List<String>> getRowsWithValueAtIndices(int[] indices, String[] values, String condition) {
			List<List<String>> retList = new ArrayList<List<String>>();
			for (List<String> row : this.getRows()) {
				for (int i = 0; i < indices.length; i++) {
				    if (row.size() <= indices[i]) {
					    break;
				    }
				    if (row.get(indices[i]).equals(values[i])) {
				    	if ("OR".equals(condition) || i + 1 >= indices.length) {
				    		retList.add(row);
				    	}
				    } else if (!"OR".equals(condition)) {
				    	break;
				    }
				}
			}
			return retList;
		}
		
		public void updateRowsWithValueAtIndex(int[] indices, String[] values, String condition, String targetColumns[], String newValues[]) {
			List<String> currentRow;
			int c = this.getRowCount();
			int targetIndices[] = new int[targetColumns.length];
			for (int i = 0; i < targetIndices.length; i++) {
				targetIndices[i] = this.getIndexForHeaderName(targetColumns[i]);
			}
			for (int i = 0; i < c; i++) {
				currentRow = this.getRows().get(i);
				for (int j = 0; j < indices.length; j++) {
				    if (currentRow.size() <= indices[j]) {
					    break;
				    }
				    if (currentRow.get(indices[j]).equals(values[j])) {
				    	if ("OR".equals(condition) || i + 1 >= indices.length) {
				    		for (int k = 0; k < targetIndices.length; k++) {
					    		this.setCellValueAtRow(i, targetIndices[k], newValues[k]);
					    	}
				    	}
				    } else if (!"OR".equals(condition)) {
				    	break;
				    }
				}
			}
		}
		
		public void deleteRowsWithValueAtIndex(int[] indices, String[] values, String condition) {
			List<String> currentRow;
			int c = this.getRowCount();
			List<Integer> delIndices = new ArrayList<Integer>();
			for (int i = 0; i < c; i++) {
				currentRow = this.getRows().get(i);
				for (int j = 0; j < indices.length; j++) {
				    if (currentRow.size() <= indices[j]) {
					    break;
				    }
				    if (currentRow.get(indices[j]).equals(values[j])) {
				    	if ("OR".equals(condition) || i + 1 >= indices.length) {
				    		delIndices.add(i);
				    	}
				    } else if (!"OR".equals(condition)) {
				    	break;
				    }
				}
			}
			Collections.sort(delIndices);
			Collections.reverse(delIndices);
			for (int i : delIndices) {
				this.getRows().remove(i);
			}
		}
		
		public void setCellValueAtRow(int rowIndex, int cellIndex, String value) {
			List<String> row = this.getRows().get(rowIndex);
			row.set(cellIndex, value);
		}
		
		public void addRow(List<String> row) {
			this.getRows().add(row);
		}
		
		public int getIndexForHeaderName(String name) {
			Integer index = this.header.get(name);
			if (index == null) {
				throw new RuntimeException("Invalid header name: " + name);
			}
			return index;
		}
		
		public List<String> getHeader() {
			List<Entry<String, Integer>> entryList = new ArrayList<Entry<String, Integer>>(this.header.entrySet());
			Collections.sort(entryList, new Comparator<Entry<String, Integer>>() {
				@Override
				public int compare(Entry<String, Integer> lhs, Entry<String, Integer> rhs) {
					return lhs.getValue() - rhs.getValue();
				}
			});
			List<String> retList = new ArrayList<String>();
			for (Entry<String, Integer> entry : entryList) {
				retList.add(entry.getKey());
			}
			return retList;
		}
		
		public List<List<String>> getRows() {
			return rows;
		}
		
		public void setRows(List<List<String>> rows) {
			this.rows = rows;
		}
		
		public int getRowCount() {
			return this.getRows().size();
		}
		
	}
	
	protected static int getMaxCols(List<List<String>> data) {
		int max = 0, tmp;
		for (List<String> entry : data) {
			tmp = entry.size();
			if (tmp > max) {
				max = tmp;
			}
		}
		return max;
	}
	
	protected static List<String> getNumberBasedHeader(int size) {
		List<String> header = new ArrayList<String>();
		for (int i = 0; i < size; i++) {
			header.add("" + i);
		}
		return header;
	}
	
}
